/*
 * CloudBank Mobile Knowledge Sharing for Language Learners
 * Supported by the JISC Rapid Innovation Programme #jiscri
 *
 * Copyright (C) 2010 University of Brighton
 *
 * Licensed under the GNU General Public License v3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/gpl.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package uk.ac.brighton.itrg.cloudbank;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;



public final class HTTPPost 
{	
	// Timeouts
	private final static int CONN_TIMEOUT = 10000; 
	private final static int SOCK_TIMEOUT = 10000; 

	// Retry on time-out
	private final static int POST_ATTEMPTS = 3;
	
	// Requests
	public static final class Request
	{
		public static final String GET_ITEM_LIST 	= "getItemList";
		public static final String GET_TAG_LIST 	= "getTagList";
		public static final String GET_ITEM 		= "getItem";
		public static final String PUT_ITEM 		= "putItem";
		public static final String FLAG_ITEM 		= "flagItem";
		public static final String LOG_INFO 		= "logInfo";
	}

	
	// Local variables
	private HttpClient mClient;
	private PostMethod mPost;
	private ArrayList mParts;
	
	
	public HTTPPost(String request)
	{
		try
		{
			mClient = new HttpClient();
			mClient.getHttpConnectionManager().getParams().setConnectionTimeout(CONN_TIMEOUT); 
			mClient.getHttpConnectionManager().getParams().setSoTimeout(SOCK_TIMEOUT); 

			// generate a call-ID and signature 	
			// String cid = (new SimpleDateFormat("yyyyMMddHHmmss")).format(new Date());
			String cid = CB.getRandom(32);
			String sig = CB.md5(cid + CB.API_SECRET);

			// create POST request with standard parameters
			mPost = new PostMethod(CB.getRestServer());
			mParts = new ArrayList();
			addString("ver", CB.API_VERSION);
			addString("key", CB.API_KEY);
			addString("uid", CB.API_UID);
			addString("cid", cid);
			addString("sig", sig);
			addString("fmt", CB.API_FORMAT);
			addString("q", request);
		}
		catch(Exception e)
		{
			Logger.log(e);
		}
	}
	
	
	public void addInteger(String name, int i)	{addString(name, Integer.toString(i));}
	public void addLong(String name, long l)	{addString(name, Integer.toString((int)l));}
	public void addString(String name, String value)
	{
		try
		{
			mParts.add(new StringPart(name, URLEncoder.encode(value, CB.ENCODING)));
		}
		catch(Exception e)
		{
			Logger.log(e);
		}
	}
	public void addFile(String name, File f, String mimetype)
	{
		try
		{
			Logger.log("HTTPost.addFile()");
			Logger.log("name", name);
			Logger.log("f", f.getAbsoluteFile());
			Logger.log("mimetype", mimetype);		
			
			byte[] data = getBytesFromFile(f);
			
			FilePart fp = new FilePart(name, new ByteArrayPartSource(name, data));
			fp.setContentType(mimetype);
            fp.setCharSet(null); 
			mParts.add(fp);
		}
		catch(Exception e)
		{
			Logger.log(e);
		}
	}
	
	
	public String post()
	{
		String response = null;
		int attempt = 0;
		
		// prepare POST
		Part[] parts = new Part[mParts.size()];
		for(int i = 0; i<mParts.size();i++)
			parts[i] = (Part)(mParts.get(i));
		mPost.setRequestEntity(new MultipartRequestEntity(parts, mPost.getParams()));

		// execute POST, eventually retry on connection error
		while((response == null)  && (attempt < POST_ATTEMPTS))
		{
			try
			{
				attempt++;
				mClient.executeMethod(mPost);
				response = mPost.getResponseBodyAsString();
				mPost.releaseConnection(); 
			}
			catch(IOException e)
			{
				// might be just a Socket or Connection timeout
				continue;
			}
			catch(Exception e)
			{
				Logger.log(e);
			}
		}
		
		return response;
	}
	

	
	// ------------------------------------------
	// utility: read file into byte array
	// ------------------------------------------
	private byte[] getBytesFromFile(File f) throws Exception 
	{
        // Check file
		if(!f.exists()) throw new Exception("File not found: "+f.getName());
		
        // Check file size		
        long length = f.length();
        if (length > Integer.MAX_VALUE) throw new Exception("File too large: "+f.getName());
        
        // Create the byte array to hold the data
        byte[] bytes = new byte[(int)length];
        InputStream is = new FileInputStream(f);
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) 
        {
            offset += numRead;
        }
        is.close();
    
        // Ensure all the bytes have been read in
        if (offset < bytes.length) throw new Exception("Could not completely read file "+f.getName());
    
        return bytes;
    }

}
